<template>
    <div class="wrapper">
        <div id="map" class="map"></div>

        <div class="btns">
            <el-button @click="drawHandle('Point')" type="primary" size="small">点</el-button>
            <el-button @click="drawHandle('LineString')" type="primary" size="small">线</el-button>
            <el-button @click="drawHandle('Circle')" type="primary" size="small">园</el-button>
            <el-button @click="drawHandle('Polygon')" type="primary" size="small">多边形</el-button>
            <el-button @click="drawHandle('Box')" type="primary" size="small">矩形</el-button>
            <el-button @click="resetDraw" v-if="state.drawing" type="primary" size="small">撤销</el-button>
            <el-button @click="clearHandle" type="primary" size="small">清空</el-button>
        </div>
    </div>
</template>

<script lang="ts" setup>
import { log } from 'console';
import { nanoid } from 'nanoid';
import { Map, View } from 'ol';
import { click } from 'ol/events/condition';
import { Circle, SimpleGeometry } from 'ol/geom';
import { Draw, Modify, Select, Snap } from 'ol/interaction';
import { createBox } from 'ol/interaction/Draw';
import TileLayer from 'ol/layer/Tile';
import VectorLayer from 'ol/layer/Vector';
import { XYZ } from 'ol/source';
import VectorSource from 'ol/source/Vector';
import { Fill, Icon, Stroke, Style } from 'ol/style';
import { onMounted, onUnmounted, reactive, ref } from 'vue';

const map = ref();
const source = ref();
const layer = ref();
const uid = ref(nanoid());
const drawer = ref();
const select = ref();
const snap = ref();
const modify = ref();
const style = new Style({
    fill: new Fill({ color: 'rgba(255,50,50,0.3)' }),
    stroke: new Stroke({
        color: 'rgba(255,50,50,0.3)',
        width: 4
    }),
    image: new Icon({
        src: new URL('../../assets/map-point.png', import.meta.url).href,
        width: 32,
        height: 32
    })
});
const selectStyle = new Style({
    fill: new Fill({ color: 'rgba(67,131,213,0.7)' }),
    stroke: new Stroke({
        color: 'rgba(67,131,213,0.7)',
        width: 4
    }),
    image: new Icon({
        src: new URL('../../assets/map-point-active.png', import.meta.url).href,
        width: 32,
        height: 32
    })
});

const state = reactive<{
    drawing: boolean;
    freehand: boolean;
}>({
    drawing: false,
    freehand: false
});

onMounted(() => {
    map.value = new Map({
        target: 'map',
        view: new View({
            center: [116.4, 39.9],
            zoom: 12,
            projection: 'EPSG:4326'
        }),
        layers: [
            new TileLayer({
                source: new XYZ({
                    url: `http://wprd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=7`
                })
            })
        ]
    });

    source.value = new VectorSource({ wrapX: true });
    layer.value = new VectorLayer({
        source: source.value,
        style: style
    });
    layer.value.set('uid', uid.value);
    map.value.addLayer(layer.value);
});

onUnmounted(() => {
    controlDispose();
    if (select.value) {
        map.value.removeInteraction(select.value);
        select.value.dispose();
    }
    if (layer.value) {
        layer.value.getSource().clear(true);
        map.value.removeLayer(layer.value);
        layer.value.dispose();
    }
});

function drawHandle(
    type: 'Point' | 'LineString' | 'Circle' | 'Box' | 'Polygon'
) {
    controlDispose();
    drawer.value = new Draw({
        type: type === 'Box' ? 'Circle' : type,
        geometryFunction: type === 'Box' ? createBox() : undefined,
        source: source.value
    });
    drawer.value.on('drawstart', () => {
        state.drawing = true;
    });
    drawer.value.on('drawend', (e) => {
        const geometry = e.feature.getGeometry() as SimpleGeometry;
        const type = geometry.getType();
        if (type === 'Circle') {
            const circle = geometry as Circle;
            console.log(circle.getCenter(), circle.getRadius());
        } else {
            console.log(geometry.getCoordinates());
        }

        state.drawing = false;
        drawer.value.setActive(false);
    });
    map.value.addInteraction(drawer.value);

    select.value = new Select({
        style: selectStyle,
        condition: function (mapBrowserEvent) {
            return click(mapBrowserEvent);
        },
        layers: (layer) => {
            return layer.get('uid') === uid.value;
        }
    });
    select.value.on('select', (e) => {
        console.log(e.selected[0], 'select=');
    });
    map.value.addInteraction(select.value);

    snap.value = new Snap({
        source: source.value
    });
    map.value.addInteraction(snap.value);

    modify.value = new Modify({
        source: source.value
    });
    modify.value.on('modifyend', (e) => {
        console.log(e.features, 'modifyend=', type);
    });
    map.value.addInteraction(modify.value);
}

function clearHandle() {
    layer.value.getSource().clear();
    modify.value.features_.clear();
}

function resetDraw() {
    drawer.value.removeLastPoint();
    drawer.value.changed();
}

function controlDispose() {
    if (drawer.value) {
        drawer.value.setActive(false);
        map.value.removeInteraction(drawer.value);
        drawer.value.dispose();
        map.value.interactions.forEach((i) => {
            if (i instanceof Draw) {
                map.value.interactions.remove(i);
            }
        });
    }
    if (snap.value) {
        snap.value.setActive(false);
        map.value.removeInteraction(snap.value);
        snap.value.dispose();
        map.value.interactions.forEach((i) => {
            if (i instanceof Snap) {
                map.value.interactions.remove(i);
            }
        });
        snap.value = undefined;
    }
    if (modify.value) {
        modify.value.setActive(false);
        map.value.removeInteraction(modify.value);
        modify.value.dispose();
    }
}
</script>

<style lang="scss" scoped>
.wrapper {
    position: relative;
    height: 100%;
    .map {
        height: 100%;
    }
    .btns {
        position: absolute;
        top: 20px;
        left: 20px;
    }
}
</style>